/*
 * Dear Lua,
 *     You are awesome. However, I simply can't stand going through your
 * reference manual anymore, looking at 50 different lua* to find what
 * I want. Therefor, I am going to attempt to make you easier to use
 * than you already are.
 *                        Forever your friend,
 *                            Endeavormac
 */
#include "lua.h"



char * mW_lua_copy_string_from_stack (lua_State * l_vm, int idx)
{
	
	size_t string_length;
	const char * string;
	char * result_string;
	
	if (l_vm == NULL)
		return NULL;
	
	string = lua_tolstring(l_vm, idx, &string_length);
	
	result_string = (char *) malloc(sizeof(char *) * (string_length + 1));
	if (result_string == NULL)
		return NULL;
	
	memset(result_string, 0, string_length + 1);
	strcpy(result_string, string);
	
	return result_string;
	
}



lua_State * mW_lua_create ()
{
	
	lua_State * l_vm = luaL_newstate();
	luaL_openlibs(l_vm);
	return l_vm;
	
}



void mW_lua_destroy (lua_State * l_vm)
{
	
	lua_close(l_vm);
	
}



int mW_lua_integer_to_int (lua_Integer integer)
{
	
	return integer % 1000000;
	
}



/*
 * Table should be at the top of the stack before you call this
 */
mW_lua_variable * mW_lua_get_table (lua_State * l_vm)
{
	
	// c_table, opposed to lua_table
	mW_list * c_table;
	mW_lua_variable * variable;
	// used to hold a mW_lua_get_table result temporarily
	mW_lua_variable * tmp_variable;
	int integer;
	
	if (l_vm == NULL)
		return NULL;
	
	// make sure what user requested is actually a table
	if (lua_istable(l_vm, -1) == 0)
		return NULL;
		
	c_table = mW_list_create();
	
	// we are now going to iterate over the table one element at a time
	
	// http://www.lua.org/manual/5.1/manual.html#lua_next
	lua_pushnil(l_vm); // this places our table at -2
	
	while (lua_next(l_vm, -2) != 0)
	{
		
		variable = (mW_lua_variable *) malloc(sizeof(mW_lua_variable));
		if (variable == NULL)
			mW_error("Error allocating memory for mW_lua_variable");
			
		
		// we're going to handle the element key first
		// it should be a number, or a string
		switch (lua_type(l_vm, -2))
		{
			
			// the key is a number
			case LUA_TNUMBER :
			
				variable->key_type = mW_LUA_TYPE_INTEGER;
				
				variable->key = (int *) malloc(sizeof(int));
				if (variable->key == NULL)
					mW_error("Error allocating memory for table key");
				integer = mW_lua_integer_to_int(lua_tointeger(l_vm, -2));
				memcpy(variable->key, &integer, sizeof(int));
				
				#if DEBUG_LUA == 1
					printf("table key %d\n", integer);
				#endif
				
				break;
			
			case LUA_TSTRING :
			
				variable->key_type = mW_LUA_TYPE_STRING;
				
				variable->key = mW_lua_copy_string_from_stack(l_vm, -2);
				if (variable->key == NULL)
					mW_error("Error copying string for table key");
					
				#if DEBUG_LUA == 1
					printf("table_key %s\n", (char *) variable->key);
				#endif
				
				break;
				
			default :
			
				mW_error_nonfatal("Invalid table key type detected");
				variable->key_type = mW_LUA_TYPE_INVALID;
				
				break;
				
		}
		
		
		// time to deal with the element value
		// it should be a number, string or table
		switch(lua_type(l_vm, -1))
		{
			
			case LUA_TNUMBER :
				
				variable->data_type = mW_LUA_TYPE_INTEGER;
				
				variable->data = (int *) malloc(sizeof(int));
				if (variable->data == NULL)
					mW_error("Error allocating memory for table data");
				integer = mW_lua_integer_to_int(lua_tointeger(l_vm, -1));
				memcpy(variable->data, &integer, sizeof(int));
				
				#if DEBUG_LUA == 1
					printf("data %d\n", integer);
				#endif
				
				break;
			
			case LUA_TSTRING :
				
				variable->data_type = mW_LUA_TYPE_STRING;
				
				variable->data = mW_lua_copy_string_from_stack(l_vm, -1);
				if (variable->data == NULL)
					mW_error("Error copying string for table data");
				
				#if DEBUG_LUA == 1
					printf("data %s\n", (char *) variable->data);
				#endif
				
				break;
			
			case LUA_TTABLE :
				
				variable->data_type = mW_LUA_TYPE_TABLE;
				
				#if DEBUG_LUA == 1
					printf("data table\n");
				#endif
				
				// the table is the value of this row, so it should already
				//   be at the top of the stack
				tmp_variable = mW_lua_get_table(l_vm);
				variable->data = tmp_variable->data;
				free(tmp_variable);
				
				break;
			
			default :
			
				mW_error_nonfatal("Invalid table data type detected");
				variable->data_type = mW_LUA_TYPE_INVALID;
				
				break;
				
		}
		
		
		// if there was an error with types
		if ((variable->key_type == mW_LUA_TYPE_INVALID) || (variable->data_type == mW_LUA_TYPE_INVALID))
		{
			mW_lua_variable_destroy(variable);	
		}
		else
		{
			mW_list_insert(c_table, variable);
		}
		
		// this removes the table element value from the top of the stack,
		//   leaving the key, which lua will use to iterate the table further
		lua_pop(l_vm, 1);
		
	}
	
	// create a variable of type table to hold this table
	
	variable = (mW_lua_variable *) malloc(sizeof(mW_lua_variable));
	if (variable == NULL)
		mW_error("Error allocating memory for variable");
		
	variable->key_type = mW_LUA_TYPE_INVALID;
	variable->key = NULL;
	variable->data_type = mW_LUA_TYPE_TABLE;
	variable->data = c_table;
	
	// our lua stack should be just as we left it
	
	return variable;
	
}	
	


int mW_lua_p_call (lua_State * l_vm, int nargs, int nresults)
{
	
	int error;
	char * error_string;
	
	if (l_vm == NULL)
		return 0;
	
	error = lua_pcall(l_vm, nargs, nresults, 0);
	
	if (error == 0)
		return 1;
		
	error_string = mW_lua_copy_string_from_stack(l_vm, -1);
	mW_lua_script_error(error_string);
	free(error_string);
	lua_settop(l_vm, -1);
	return 0;
	
}
		


int mW_lua_run_file (lua_State * l_vm, const char * filename)
{
	
	struct stat statbuf;
	int error;
	
	if (l_vm == NULL)
		return 0;
		
	if (stat(filename, &statbuf) != 0)
		return 0;
	
	error = luaL_loadfile(l_vm, filename);
	
	if (error == 0)
	{
		
		error = mW_lua_p_call (l_vm, 0, 0);
		
		if (error == 1)
			return 1;
		else
			return 0;
		
	}	
	else if (error == LUA_ERRMEM)
		mW_error("Could not allocate lua memory");
	else if (error == LUA_ERRSYNTAX)
		mW_error("Error in lua syntax");
	
	return 0;
	
}



void mW_lua_script_error (char * text)
{
	
	printf("%s\n", text);
	
}



void mW_lua_variable_destroy (mW_lua_variable * variable)
{
	
	mW_lua_variable_clean (variable);

	free (variable);
	
}


/*
 * This frees all memory held by variable, but doesn't free variable
 *   itself
 */
void mW_lua_variable_clean (mW_lua_variable * variable)
{
	
	mW_lua_variable * table_variable;
	
	if (variable->key_type == mW_LUA_TYPE_INTEGER)
		free(variable->key);
	else if (variable->key_type == mW_LUA_TYPE_STRING)
		free(variable->key);
	
	if (variable->data_type == mW_LUA_TYPE_INTEGER)
		free(variable->data);
	else if (variable->data_type == mW_LUA_TYPE_STRING)
		free(variable->data);
	else if (variable->data_type == mW_LUA_TYPE_TABLE)
	{
		mW_list_reset_iterator(variable->data, 1);
		table_variable = mW_list_iterate(variable->data, 1);
		while (table_variable != NULL)
		{
			mW_lua_variable_clean(table_variable);
			table_variable = mW_list_iterate(variable->data, 1);
		}
		mW_list_destroy(variable->data);
	}
	
}
